home *** CD-ROM | disk | FTP | other *** search
/ Developer CD Series 1996 July: Mac OS SDK / Dev.CD Jul 96 SDK / Dev.CD Jul 96 SDK1.toast / Development Kits (Disc 1) / AOCE / Development Tools / Sample Code / Standard Catalog Package / DTS AddressOMatic / Src / Main.c < prev    next >
Encoding:
C/C++ Source or Header  |  1993-10-20  |  31.5 KB  |  1,268 lines  |  [TEXT/KAHL]

  1. #define USE_MAIL_ADDRESSES    1
  2. /*                                        Main.c                                    */
  3. /*
  4.  * AddressOMatic Sample
  5.  * Main.c
  6.  * Copyright © 1993 Apple Computer Inc. All rights reserved.
  7.  */
  8. #define EXTERN            /* */
  9. #ifndef USE_MAIL_ADDRESSES
  10. #define USE_MAIL_ADDRESSES    1
  11. #endif
  12. #include "AddressOMaticTest.h"
  13. #include <OCETemplates.h>
  14. #include <Palettes.h>
  15.  
  16. #ifndef topLeft
  17. #define topLeft(r)    (((Point *) &(r))[0])
  18. #define botRight(r)    (((Point *) &(r))[1])
  19. #endif
  20. #define AOMPtr(window)    ((AddressOMaticPtr) GetWRefCon(window))
  21.  
  22. typedef struct EventString {
  23.     AOMPanelState                stateBit;
  24.     StringPtr                    message;
  25. } EventString, *EventStringPtr;
  26.  
  27. /*
  28.  * kAOMFindInProgressMask is set while finding is ongoing. To prevent
  29.  * flooding the error log, we only display changes. See DisplayEventString.
  30.  */ 
  31. const EventString                    gEventString[] = {
  32.     { kAOMPanelBecomesTargetMask,        "\pPanel becomes target"        },
  33.     { kAOMAppBecomesTargetMask,            "\pApplication becomes target"    },
  34. /*     { kAOMFindInProgressMask,            "\pFind in progress"            },    */
  35.     { kAOMSelectedAnItemMask,            "\pSelected an item"            },
  36.     { kAOMChangedSelectionMask,            "\pChanged selection"            },
  37.     { kAOMDoneButtonHitMask,            "\pDone button hit"                },
  38.     { kAOMCCButtonHitMask,                "\pCC Button hit"                },
  39.     { kAOMBCCButtonHitMask,                "\pBCC Button hit"                },
  40.     { kAOMToButtonHitMask,                "\pTo Button hit"                },
  41.     { kAOMSaveButtonHitMask,            "\pSave Button hit"                },
  42.     { 0,                                NULL                            }
  43. };
  44. void                            DisplayEventString(
  45.         register BrowserPtr            browserPtr
  46.     );
  47.  
  48. void                            DumpAuxColorRecord(
  49.         register BrowserPtr            browserPtr
  50.     );
  51. void                            DumpColor(
  52.         RGBColor                    *theColor,
  53.         StringPtr                    result
  54.     );
  55.  
  56. /*
  57.  * gTypeList contains a list of record types that we can search
  58.  * for. It is initialized to "things we can send mail to."
  59.  */
  60. #define    kMaxPathParts        64
  61. RString                        *gTypeList[kMaxPathParts];
  62. short                        gTypeCount;
  63.  
  64.  
  65. /*
  66.  * These enum's define items in the various menus.
  67.  */
  68. enum AppleMenu {
  69.     kAppleAbout                = 1
  70. };
  71. enum FileMenu {
  72.     kFileCreateWindow        = 1,
  73.     kFileCloseWindow,
  74.     kFileUnused1,
  75.     kFileSetIdentity,
  76.     kFileUnused2,
  77.     kFileQuit
  78. };
  79. enum EditMenu {
  80.     kEditUndo                = 1,
  81.     kEditUnused,
  82.     kEditCut,
  83.     kEditCopy,
  84.     kEditPaste,
  85.     kEditClear
  86. };
  87.  
  88. Boolean                    gInForeground;
  89. short                    gWindowOffset = 0;
  90.  
  91. /*
  92.  * Local function prototypes.
  93.  */
  94. void                    main(void);
  95. void                    EventLoop(void);
  96. void                    ApplicationEventLoop(void);
  97. void                    DoMouseEvent(void);
  98. void                    ProcessActivateEvent(
  99.         WindowPtr                activeWindow,
  100.         Boolean                    isActivate
  101.     );
  102. void                    DoCommand(
  103.         BrowserPtr                browserPtr,
  104.         long                    menuChoice
  105.     );
  106. void                    AdjustMenus(void);
  107. void                    DeleteAllWindows(void);
  108. void                    InitMacintosh(void);
  109. void                    InitApplication(void);
  110. void                    AdjustEditMenu(
  111.         Boolean                    isDeskAcc
  112.     );
  113. void                    SetupTypesList(void);
  114. void                    MakeAddressOMaticWindow(void);
  115. void                    DisposeAddressOMatic(
  116.         BrowserPtr                browserPtr
  117.     );
  118. void                    DoContentClick(
  119.         BrowserPtr                browserPtr
  120.     );
  121. void                    DoWindowKeyDown(
  122.         BrowserPtr                browserPtr
  123.     );
  124. Boolean                    DoPanelEvent(
  125.         BrowserPtr                browserPtr
  126.     );
  127. void                    UpdateDestination(
  128.         BrowserPtr                browserPtr
  129.     );
  130. void                    ProcessAOMSelection(
  131.         register BrowserPtr        browserPtr
  132.     );
  133. void                    DisplayRecipients(
  134.         register BrowserPtr        browserPtr
  135.     );
  136. void                    DisplayOneDSSpec(
  137.         register BrowserPtr            browserPtr,
  138.         PackedDSSpecPtr                theDSSpecPtr
  139.     );
  140. /*
  141.  * This is copied from the Snippits library.
  142.  */
  143. OSErr                        GetUserIdentity(
  144.         AuthIdentity            *userIdentity
  145.     );
  146. void                        CheckForAOCEToolbox(void);
  147.  
  148. /*
  149.  * main
  150.  * The application main program.
  151.  */
  152. void
  153. main()
  154. {
  155.         InitMacintosh();
  156.         InitApplication();
  157.         CheckForAOCEToolbox();
  158.         if (gHasAOCEToolbox == FALSE) {
  159.             ErrorAlert(ALRT_Fatal, kOCEToolboxNotOpen, "\pAOCE Toolbox required");
  160.             ExitToShell();
  161.         }
  162.         gInForeground = TRUE;
  163.         SetupTypesList();
  164.         MakeAddressOMaticWindow();
  165.         InitCursor();
  166.         gUpdateMenusNeeded = TRUE;
  167.         while (gQuitNow == FALSE)
  168.             EventLoop();
  169.         ExitToShell();
  170. }
  171.  
  172. /*
  173.  * Application event loop: process one event each time.
  174.  */    
  175. void
  176. EventLoop(void)
  177. {
  178.         register BrowserPtr                browserPtr;
  179.         long                            menuChoice;
  180.         register WindowPtr                theWindow;
  181.         GrafPtr                            savePort;
  182.         Boolean                            isActivating;
  183.         Boolean                            mustHandleEvent;
  184.         long                            sleepTime;
  185.         
  186.         if (gUpdateMenusNeeded)
  187.             AdjustMenus();
  188.         if (gFindInProgress)
  189.             sleepTime = 0;
  190.         else if (gInForeground)
  191.             sleepTime = 10;
  192.         else {
  193.             sleepTime = 60;
  194.         }
  195.         WaitNextEvent(everyEvent, &EVENT, sleepTime, NULL);
  196.         theWindow = FrontWindow();
  197.         if (theWindow != NULL)
  198.             SetPort(theWindow);
  199.         if ((browserPtr = IsOurWindow(theWindow)) == NULL)
  200.             mustHandleEvent = TRUE;
  201.         else {
  202.             mustHandleEvent = DoPanelEvent(browserPtr);
  203.         }
  204.         if (mustHandleEvent) {
  205.             switch (EVENT.what) {
  206.             case nullEvent:
  207.                 break;
  208.             case keyDown:
  209.             case autoKey:
  210.                 if ((EVENT.message & charCodeMask) == '.'
  211.                  && (EVENT.modifiers & cmdKey) != 0) {
  212.                     FlushEvents(keyDown | autoKey, 0);
  213.                     gQuitNow = TRUE;
  214.                 }
  215.                 else if ((EVENT.modifiers & cmdKey) != 0) {
  216.                     if (EVENT.what == keyDown) {
  217.                         menuChoice = MenuKey(EVENT.message & charCodeMask);
  218.                         if (HiWord(menuChoice) != 0
  219.                             && (browserPtr = IsOurWindow(theWindow)) != NULL)
  220.                             DoCommand(browserPtr, menuChoice);
  221.                         else if ((browserPtr = IsOurWindow(theWindow)) != NULL)
  222.                             DoWindowKeyDown(browserPtr);
  223.                         else {
  224.                             SysBeep(10);
  225.                         }
  226.                     }
  227.                 }
  228.                 else if ((browserPtr = IsOurWindow(theWindow)) != NULL)
  229.                     DoWindowKeyDown(browserPtr);
  230.                 else {
  231.                     SysBeep(10);
  232.                 }
  233.                 break;
  234.             case mouseDown:
  235.                 DoMouseEvent();
  236.                 break;
  237.             case updateEvt:
  238.                 theWindow = (WindowPtr) EVENT.message;
  239.                 GetPort(&savePort);
  240.                 SetPort(theWindow);
  241.                 BeginUpdate(theWindow);
  242.                 EraseRect(&theWindow->portRect);
  243.                 if ((browserPtr = IsOurWindow(theWindow)) != NULL) {
  244.                     UpdateControls(theWindow, theWindow->visRgn);
  245.                     MyDrawGrowIcon(theWindow);
  246.                     AOMUpdatePanel(BROWSER.aomPtr, theWindow->visRgn);
  247.                     UpdateLog(RESULT_LOG);
  248.                     UpdateLog(ERROR_LOG);
  249.                 }
  250.                 EndUpdate(theWindow);
  251.                 SetPort(savePort);
  252.                 break;
  253.             case activateEvt:
  254.                 theWindow = (WindowPtr) EVENT.message;
  255.                 isActivating = ((EVENT.modifiers & activeFlag) != 0);
  256.                 goto activateEvent;
  257.                 break;
  258.             case osEvt:
  259.                 switch (((unsigned long) EVENT.message) >> 24) {
  260.                 case mouseMovedMessage:
  261.                     break;
  262.                 case suspendResumeMessage:
  263.                     isActivating = ((EVENT.message & 0x01) != 0);
  264. activateEvent:        if (isActivating) {
  265.                         /*
  266.                          * Activate this window. Activate events
  267.                          * define theWindow from the event record,
  268.                          * while suspend/resume uses the pre-set
  269.                          * FrontWindow value.
  270.                          */
  271.                         SelectWindow(theWindow);
  272.                         (void) TEFromScrap();
  273.                     }
  274.                     if ((browserPtr = IsOurWindow(theWindow)) != NULL) {
  275.                         ActivateLog(ERROR_LOG, isActivating);
  276.                         ActivateLog(RESULT_LOG, isActivating);
  277.                         /*
  278.                          * Globalize it so we can find the current
  279.                          * window in the debugger.
  280.                          */
  281.                         if (isActivating) {
  282.                             gCurrentBrowser = browserPtr;
  283.                             gCurrentLog = ERROR_LOG;
  284.                         }
  285.                         MyDrawGrowIcon(theWindow);
  286.                     }
  287.                     else {
  288.                         /* Desk accessory or what? */
  289.                     }
  290.                     gInForeground = isActivating;
  291.                     gUpdateMenusNeeded = TRUE;
  292.                     break;
  293.                 }
  294.                 break;
  295.             }
  296.         }
  297.         theWindow = FrontWindow();    /* Can be changed by events    */
  298.         if (IsOurWindow(theWindow)) {
  299.             SetPort(theWindow);
  300.             //** BalloonHelp(theWindow);
  301.         }
  302. }
  303.  
  304. /*
  305.  * DoMouseEvent
  306.  * The user clicked on something. Handle application-wide
  307.  * processing here, or call a Directory Browser function
  308.  * for specific action.
  309.  */
  310. void
  311. DoMouseEvent(void)
  312. {
  313.         register BrowserPtr        browserPtr;
  314.         WindowPtr                theWindow;
  315.         short                    whichPart;
  316.         short                    minimumWidth;
  317.         short                    minimumHeight;
  318.         
  319.         whichPart = FindWindow(EVENT.where, &theWindow);
  320.         if (theWindow == NULL)
  321.             theWindow = FrontWindow();
  322.         if (whichPart == inMenuBar
  323.          && (browserPtr = IsOurWindow(theWindow)) == NULL)
  324.             theWindow = FrontWindow();
  325.         browserPtr = IsOurWindow(theWindow);
  326.         if (browserPtr != NULL)
  327.             SetPort(theWindow);
  328.         switch (whichPart) {
  329.         case inDesk:
  330.             break;
  331.         case inMenuBar:
  332.             InitCursor();
  333.             if (browserPtr != NULL)
  334.                 DoCommand(browserPtr, MenuSelect(EVENT.where));
  335.             break;
  336.         case inDrag:
  337.             DragWindow(theWindow, EVENT.where, &qd.screenBits.bounds);
  338.             break;
  339.         case inGoAway:
  340.             if (TrackGoAway(theWindow, EVENT.where)) {
  341.                 if (browserPtr != NULL) {
  342.                     DisposeAddressOMatic(browserPtr);
  343.                     if (gOpenWindowCount <= 0)
  344.                         gQuitNow = TRUE;
  345.                 }
  346.                 else {
  347.                     SysBeep(10);
  348.                 }
  349.             }
  350.             break;
  351.         case inZoomIn:
  352.         case inZoomOut:
  353.             if (TrackBox(theWindow, EVENT.where, whichPart)) {
  354.                 DoZoomWindow(theWindow, whichPart);
  355.                 goto resizeWindow;
  356.             }
  357.             break;
  358.         case inGrow:
  359.             AOMGetDimensions(&minimumWidth, &minimumHeight);
  360.             minimumWidth += kPrivateWidth;
  361.             minimumHeight += kPrivateHeight;
  362.             if (DoGrowWindow(theWindow, minimumWidth, minimumHeight)) {
  363. resizeWindow:    DecorateWindow(browserPtr);
  364.             }
  365.             break;
  366.         case inContent:
  367.             if (theWindow != FrontWindow()) {
  368.                 SelectWindow(theWindow);
  369.                 if ((browserPtr = IsOurWindow(theWindow)) != NULL) {
  370.                     gCurrentBrowser = browserPtr;
  371.                     gCurrentLog = ERROR_LOG;
  372.                 }
  373.             }
  374.             else if ((browserPtr = IsOurWindow(theWindow)) != NULL) {
  375.                 DoContentClick(browserPtr);
  376.             }
  377.             else {
  378.                 /* Nothing happens here        */
  379.             }
  380.             break;
  381.         default:
  382.             break;                        /* Might be for the SDP Panel    */
  383.         }
  384. }
  385.  
  386. /*
  387.  * DoCommand
  388.  * Process a menu or keystroke command.
  389.  */
  390. void
  391. DoCommand(
  392.         register BrowserPtr        browserPtr,
  393.         long                    menuChoice
  394.     )
  395. {
  396.         short                menuItem;
  397.         Str255                menuText;
  398.         GrafPtr                savePort;
  399.         
  400.         menuItem = LoWord(menuChoice);
  401.         switch (HiWord(menuChoice)) {
  402.         case MENU_Apple:
  403.             if (menuItem == kAppleAbout)
  404.                 ;
  405.             else {
  406.                 GetItem(gAppleMenu, menuItem, menuText);
  407.                 AdjustEditMenu(TRUE);
  408.                 GetPort(&savePort);
  409.                 OpenDeskAcc(menuText);
  410.                 SetPort(savePort);
  411.                 AdjustEditMenu(browserPtr == NULL);
  412.             }
  413.             break;
  414.         case MENU_File:
  415.             switch (menuItem) {
  416.             case kFileCreateWindow:
  417.                 MakeAddressOMaticWindow();
  418.                 break;
  419.             case kFileCloseWindow:
  420.                 if (browserPtr != NULL) {
  421.                     DisposeAddressOMatic(browserPtr);
  422.                     if (gOpenWindowCount <= 0)
  423.                         gQuitNow = TRUE;
  424.                 }
  425.                 break;
  426.             case kFileSetIdentity:
  427.                 if (browserPtr != NULL && gHasAOCEToolbox)
  428.                     SetSpecificIdentity(browserPtr);
  429.                 break;            
  430.             case kFileQuit:
  431.                 gQuitNow = TRUE;
  432.                 break;
  433.             }
  434.             break;
  435.         case MENU_Edit:
  436.             if (SystemEdit(menuItem - 1) == FALSE)
  437.                 SysBeep(10);
  438.             break;
  439.         }
  440.         HiliteMenu(0);
  441. }        
  442.  
  443. /*
  444.  * AdjustMenus
  445.  * Enable/disable menu options.
  446.  */
  447. void
  448. AdjustMenus(void)
  449. {
  450.         register BrowserPtr            browserPtr;
  451.         
  452.         browserPtr = IsOurWindow(FrontWindow());
  453.         EnableItem(gFileMenu, kFileQuit);
  454.         EnableItem(gFileMenu, kFileCreateWindow);
  455.         if (browserPtr != NULL) {
  456.             EnableItem(gFileMenu, kFileCloseWindow);
  457.             if (gHasAOCEToolbox)
  458.                 EnableItem(gFileMenu, kFileSetIdentity);
  459.         }
  460.         else {
  461.             DisableItem(gFileMenu, kFileCloseWindow);
  462.             DisableItem(gFileMenu, kFileSetIdentity);
  463.         }
  464.         AdjustEditMenu(browserPtr == NULL);
  465.         gUpdateMenusNeeded = FALSE;
  466. }
  467.  
  468. /*
  469.  * AdjustEditMenu
  470.  * Enable/disable Edit Menu options.
  471.  */
  472. void
  473. AdjustEditMenu(
  474.         Boolean                isDeskAcc
  475.     )
  476. {
  477.         if (isDeskAcc) {
  478.             EnableItem(gEditMenu, kEditUndo);
  479.             EnableItem(gEditMenu, kEditCut);
  480.             EnableItem(gEditMenu, kEditCopy);
  481.             EnableItem(gEditMenu, kEditPaste);
  482.             EnableItem(gEditMenu, kEditClear);
  483.         }
  484.         else {
  485.             DisableItem(gEditMenu, kEditUndo);
  486.             DisableItem(gEditMenu, kEditCut);
  487.             DisableItem(gEditMenu, kEditCopy);
  488.             DisableItem(gEditMenu, kEditPaste);
  489.             DisableItem(gEditMenu, kEditClear);
  490.         }
  491. }
  492.  
  493. /*
  494.  * DeleteAllWindows
  495.  * This is called when the application quits to make
  496.  * sure that all windows are deleted. It isn't really
  497.  * needed until we have to handle open files.
  498.  */
  499. void
  500. DeleteAllWindows(void)
  501. {
  502.         WindowPtr                thisWindow;
  503.         WindowPeek                nextWindow;
  504.         register BrowserPtr        browserPtr;
  505.         
  506.         thisWindow = FrontWindow();
  507.         while (thisWindow != NULL) {
  508.             nextWindow = ((WindowPeek) thisWindow)->nextWindow;
  509.             if ((browserPtr = IsOurWindow(thisWindow)) != NULL)
  510.                 DisposeAddressOMatic(browserPtr);
  511.             thisWindow = (WindowPtr) nextWindow;
  512.         }
  513. }
  514.  
  515. /*
  516.  * ClearMemory
  517.  * This is a utility function that clears a block
  518.  * of memory. It isn't very efficient. It us
  519.  * normally called by executing the CLEAR macro
  520.  * which expands CLEAR(thing) to
  521.  *        ClearMemory(&thing, sizeof thing);
  522.  */
  523. void
  524. ClearMemory(
  525.         void                *recordPtr,
  526.         register Size        recordSize
  527.     )
  528. {
  529.         register char        *rp;
  530.         
  531.         rp = (char *) recordPtr;
  532.         while (recordSize > 0) {
  533.             *rp++ = 0;
  534.             --recordSize;
  535.         }
  536. }
  537.  
  538. /*
  539.  * InitMacintosh
  540.  * Perform the normal application initialization.
  541.  * This should probably be extended to add a
  542.  * "rainy day" memory fund. The only thing
  543.  * this module does is initialize the managers.
  544.  */
  545. void
  546. InitMacintosh(void)
  547. {
  548.         int                i;
  549.         long            response;
  550.         
  551.         MaxApplZone();        
  552.         InitGraf(&qd.thePort);
  553.         InitFonts();
  554.         InitWindows();
  555.         InitMenus();
  556.         TEInit();
  557.         InitDialogs(NULL);
  558.         HNoPurge((Handle) GetCursor(watchCursor));
  559.         SetCursor(*GetCursor(watchCursor));
  560.         if (Gestalt(gestaltQuickdrawFeatures, &response) == noErr
  561.          && (response & (1 << gestaltHasColor)) != 0)
  562.              gHasColorQuickDraw = TRUE;
  563.         for (i = 0; i < 3; i++)
  564.             EventAvail(everyEvent, &EVENT);
  565. }
  566.  
  567. /*
  568.  * InitApplication
  569.  * Continue initialization. Failure dies a horrible death.
  570.  */
  571. void
  572. InitApplication(void)
  573. {
  574.         Handle            menuBarHdl;
  575.  
  576.         (void) TEFromScrap();
  577.         menuBarHdl = GetNewMBar(MBAR_MenuBar);
  578.         SetMenuBar(menuBarHdl);
  579.         gAppleMenu = GetMHandle(MENU_Apple);
  580.         gFileMenu = GetMHandle(MENU_File);
  581.         gEditMenu = GetMHandle(MENU_Edit);
  582.         AddResMenu(GetMHandle(MENU_Apple), 'DRVR');
  583.         DrawMenuBar();
  584. }
  585.  
  586. /*
  587.  * IsOurWindow
  588.  * Return BrowserPtr if this is a directory browser window.
  589.  */
  590. BrowserPtr
  591. IsOurWindow(
  592.         WindowPtr            theWindow
  593.     )
  594. {
  595.         if (theWindow == NULL
  596.          || ((WindowPeek) theWindow)->windowKind != userKind)
  597.             return (NULL);
  598.         else {
  599.             return ((BrowserPtr) theWindow);
  600.         }
  601. }
  602.  
  603. /*
  604.  *** AddressOMatic stuff
  605.  */
  606.  
  607. /*
  608.  * SetupTypesList creates a list of "addressable" objects.
  609.  */
  610. void
  611. SetupTypesList(void)
  612. {
  613.         OSErr                        status;
  614.         RString                        addressCategory;
  615.         PackedRStringListHandle        categoryTypes;
  616.     
  617.         OCECToRString(
  618.             kDETCategoryAddressItems,
  619.             smRoman,
  620.             &addressCategory,
  621.             kRStringMaxBytes
  622.         );
  623.         status = SDPGetCategoryTypes(&addressCategory, &categoryTypes);
  624.         /*
  625.          * Get a private copy of the category types so we can lock
  626.          * it down in memory without concern that someone will unlock it.
  627.          */
  628.         if (status == noErr)
  629.             status = HandToHand((Handle *) &categoryTypes);
  630.         if (status == noErr) {
  631.             MoveHHi((Handle) categoryTypes);
  632.             HLock((Handle) categoryTypes);
  633.             gTypeCount = OCEUnpackPathName(*categoryTypes, gTypeList, kMaxPathParts);
  634.         }
  635.         if (status != noErr)
  636.             ErrorAlert(ALRT_NonFatal, status, "\pSetupTypesList");
  637. }
  638.  
  639. void
  640. MakeAddressOMaticWindow(void)
  641. {
  642.         OSErr                            status;
  643.         Rect                            boundsRect;
  644.         WindowPtr                        theWindow;
  645.         short                            minimumWidth;
  646.         short                            minimumHeight;
  647.         BrowserPtr                        browserPtr;
  648.         long                            response;
  649.         Boolean                            hasColorQuickDraw;
  650.         
  651.         
  652.         SetCursor(*GetCursor(watchCursor));
  653.         hasColorQuickDraw = FALSE;
  654.         if (Gestalt(gestaltQuickdrawVersion, &response) == noErr
  655.          && response >= gestalt8BitQD)
  656.              hasColorQuickDraw = TRUE;
  657.         browserPtr = (BrowserPtr) NewPtrClear(sizeof (BrowserRecord));
  658.         if (browserPtr == NULL) {
  659.             ErrorAlert(ALRT_Continue, MemError(), "\pCreate browser record");
  660.             return;
  661.         }
  662.         theWindow = NULL;
  663.         /*
  664.          * Ignore errors from GetUserIdentity for testing.
  665.          */
  666.         (void) GetUserIdentity(&BROWSER.userIdentity);
  667.         AOMGetDimensions(&minimumWidth, &minimumHeight);
  668.         boundsRect = qd.screenBits.bounds;
  669.         InsetRect(&boundsRect, 4, 4);
  670.         boundsRect.top += (GetMBarHeight() * 2);
  671.         boundsRect.bottom = boundsRect.top + minimumHeight + kPrivateHeight;
  672.         boundsRect.right = boundsRect.left + minimumWidth + kPrivateWidth;
  673.         OffsetRect(&boundsRect, gWindowOffset, gWindowOffset);
  674.         gWindowOffset += 16;
  675.         if (hasColorQuickDraw) {
  676.             theWindow = NewCWindow(
  677.                     &WINDOW,                        /* Use our storage                */
  678.                     &boundsRect,                    /* Initial bounds                */
  679.                     "\pAddressOMatic™ Test",        /* Title                        */
  680.                     TRUE,                            /* Make visible                    */
  681.                     zoomDocProc,                    /* Normal document                */
  682.                     (WindowPtr) -1L,                /* In front                        */
  683.                     TRUE,                            /* Has GoAway box                */
  684.                     0                                /* No refCon yet                */
  685.                 );
  686.         }
  687.         else {
  688.             theWindow = NewWindow(
  689.                     &WINDOW,                        /* Use our storage                */
  690.                     &boundsRect,                    /* Initial bounds                */
  691.                     "\pAddressOMatic™ Test",        /* Title                        */
  692.                     TRUE,                            /* Make visible                    */
  693.                     zoomDocProc,                    /* Normal document                */
  694.                     (WindowPtr) -1L,                /* In front                        */
  695.                     TRUE,                            /* Has GoAway box                */
  696.                     0                                /* No refCon yet                */
  697.                 );
  698.         }
  699.         if (theWindow != (WindowPtr) &WINDOW) {
  700.             ErrorAlert(ALRT_Continue, MemError(), "\pCreate browser window");
  701.             return;
  702.         }
  703.         SetPort((GrafPtr) &WINDOW);
  704.         DecorateWindow(browserPtr);
  705.         ValidRect(&WINDOW.port.portRect);
  706.         ERROR_LOG = CreateLog(&BROWSER.logRect, geneva,    9, 0);
  707.         gCurrentLog = ERROR_LOG;
  708.         status = noErr;
  709.         if (ERROR_LOG == NULL) {
  710.             status = MemError();
  711.             ErrorAlert(ALRT_Continue, status, "\pCreate error log");
  712.         }
  713.         RESULT_LOG = CreateLog(&BROWSER.destRect, geneva, 9, 0);
  714.         if (RESULT_LOG == NULL) {
  715.             status = MemError();
  716.             ErrorAlert(ALRT_Continue, status, "\pCreate recipient log");
  717.         }
  718.         if (status == noErr) {
  719.             /*
  720.              * Draw the logs when we start as AOMNewPanel takes a while
  721.              */
  722.             UpdateLog(ERROR_LOG);
  723.             UpdateLog(RESULT_LOG);
  724.             SetCursor(*GetCursor(watchCursor));
  725.             status = AOMNewPanel(
  726.                     &PANEL,                        /* Result                        */
  727.                     theWindow,                    /* In this window                */
  728.                     &BROWSER.panelRect,            /* In this panel                */
  729.                     TRUE,                        /* Initially visible            */
  730.                     TRUE,                        /* Initially enabled            */
  731.                     BROWSER.userIdentity,        /* Current identity                */
  732. #if USE_MAIL_ADDRESSES
  733.                     gTypeList,                    /* Addressable types            */
  734.                     gTypeCount,                    /* Zero type count implies all    */
  735. #else
  736.                     NULL,                        /* All types                    */
  737.                     0,                            /* Zero type count                */
  738. #endif
  739.                     0,                            /* No refCon                    */
  740.                     ( kAOMAllActionButtons        /* All action buttons             */
  741.                         | kAOMStandardRadioButtons    /* And normal radio buttons    */
  742.                         | kAOMAllRadioButtons    /* All radio buttons            */
  743.                     ),
  744.                     kAOMEnablePanelBit,            /* Initial radio button mode    */
  745.                     STRN_AOMLabels,                /* Item labels                    */
  746.                     STRN_BrowsePanelHelpStrings, /* Browser help STR# resource    */
  747.                     STRN_PDPanelHelpStrings,    /* Personal catalog help string    */
  748.                     STRN_FindPanelHelpStrings,    /* Find Panel help strings        */
  749.                     NULL,                        /* Initial RLI (none)            */
  750. #if USE_MAIL_ADDRESSES
  751.                     ( kEnumDistinguishedNameMask    /* Enumeration choices        */
  752.                         | kEnumAliasMask
  753.                         | kEnumDNodeMask
  754.                     ),
  755.                     kExactMatch,                /* DirMatchWith                    */
  756. #else
  757.                     kEnumAllMask,                /* All possible enumerations    */
  758.                     kMatchAll,                    /* All matches                    */
  759. #endif
  760.                     1                            /* Simultaneous search count    */
  761. #ifdef ENABLE_TYPEIN
  762.                     ,
  763.                     OCEGetIndRecordType(kUserRecTypeNum) /* Type-in record ID    */
  764. #endif
  765.                 );
  766.         }
  767. #if 0
  768.         if (hasColorQuickDraw)                    /* Debug only.                    */
  769.             DumpAuxColorRecord(browserPtr);
  770. #endif
  771.         InitCursor();
  772.         if (status == noErr) {
  773.             ShowWindow(theWindow);
  774.             SelectWindow(theWindow);
  775.             gCurrentBrowser = browserPtr;
  776.             ActivateLog(ERROR_LOG, TRUE);
  777.             ActivateLog(RESULT_LOG, TRUE);
  778.             ++gOpenWindowCount;
  779.         }
  780.         else {
  781.             ErrorAlert(ALRT_Continue, status, "\pCreating AOM Panel");
  782.             gCurrentLog = NULL;
  783.             if (theWindow != NULL) {
  784.                 if (PANEL != NULL)
  785.                     AOMDisposePanel(PANEL);
  786.                 DisposeLog(RESULT_LOG);
  787.                 DisposeLog(ERROR_LOG);
  788.                 CloseWindow((WindowPtr) &WINDOW);
  789.             }
  790.             if (browserPtr != NULL)
  791.                 DisposePtr((Ptr) browserPtr);
  792.         }
  793.         InitCursor();
  794. }
  795.     
  796. void
  797. DecorateWindow(
  798.         register BrowserPtr            browserPtr
  799.     )
  800. {
  801.         InvalRect(&WINDOW.port.portRect);
  802.         BROWSER.panelRect = WINDOW.port.portRect;
  803.         BROWSER.panelRect.top += kSlop;
  804.         BROWSER.panelRect.left += kScrollBarWidth;
  805.         BROWSER.panelRect.right -= kScrollBarWidth;
  806.         BROWSER.panelRect.bottom -= kPrivateHeight;
  807.         BROWSER.destRect = WINDOW.port.portRect;
  808.         BROWSER.destRect.top = BROWSER.panelRect.bottom + kSlop;
  809.         BROWSER.destRect.bottom = BROWSER.destRect.top + kDestinationHeight;
  810.         BROWSER.destRect.right += 1;
  811.         BROWSER.logRect = WINDOW.port.portRect;
  812.         BROWSER.logRect.top = BROWSER.destRect.bottom + kSlop;
  813.         BROWSER.logRect.bottom = WINDOW.port.portRect.bottom - kScrollBarWidth + 1;
  814.         BROWSER.logRect.right += 1;
  815.         if (PANEL != NULL)
  816.             (void) AOMAdjustPanel(PANEL, &BROWSER.panelRect);
  817.         if (ERROR_LOG != NULL) {
  818.             MoveLog(
  819.                 ERROR_LOG,
  820.                 BROWSER.logRect.left,
  821.                 BROWSER.logRect.top
  822.             );
  823.             SizeLog(
  824.                 ERROR_LOG,
  825.                 width(BROWSER.logRect),
  826.                 height(BROWSER.logRect)
  827.             );
  828.         }
  829.         if (RESULT_LOG != NULL) {
  830.             MoveLog(
  831.                 RESULT_LOG,
  832.                 BROWSER.destRect.left,
  833.                 BROWSER.destRect.top
  834.             );
  835.             SizeLog(
  836.                 RESULT_LOG,
  837.                 width(BROWSER.destRect),
  838.                 height(BROWSER.destRect)
  839.             );
  840.         }
  841. }
  842.  
  843. void
  844. SetSpecificIdentity(
  845.         BrowserPtr                browserPtr
  846.     )
  847. {
  848.         OSErr                    status;
  849.         AuthIdentity            userIdentity;
  850.         SDPIdentityKind            selectedKind;
  851.         
  852.         status = SDPPromptForID(
  853.                 &userIdentity,                /* AuthIdentity        *id                */ 
  854.                 NULL,                        /* Default guest prompt                */
  855.                 NULL,                        /* Default specific prompt            */
  856.                 NULL,                        /* Default local prompt                */
  857.                 NULL,                        /* RString            *recordType        */
  858.                 (                            /* SDPIdentityKind    permittedKinds    */
  859.                     kSDPLocalIdentityMask        /* Local identity                */
  860.                     | kSDPSpecificIdentityMask    /* or specific identity            */
  861.                     | kSDPGuestMask                /* or guest identity            */
  862.                 ),
  863.                 &selectedKind,                /* SDPIdentityKind    *selectedKind    */
  864.                 NULL,                        /* RecordID            *loginFilter    */
  865.                 0                            /* SDPLoginFilterKind filterKind    */                        
  866.             );
  867.         if (status == noErr) {
  868.             status = AOMSetIdentity(BROWSER.aomPtr, userIdentity);
  869.         }
  870. }
  871.  
  872. void
  873. DisposeAddressOMatic(
  874.         BrowserPtr                browserPtr
  875.     )
  876. {
  877.         (void) AOMDisposePanel(BROWSER.aomPtr);
  878.         DisposeLog(RESULT_LOG);
  879.         DisposeLog(ERROR_LOG);
  880.         CloseWindow((WindowPtr) &WINDOW);
  881.         DisposePtr((Ptr) browserPtr);
  882.         --gOpenWindowCount;
  883.         if (gOpenWindowCount <= 0)
  884.             gQuitNow = TRUE;
  885. }
  886.  
  887. /*
  888.  * We don't have any content click actions.
  889.  */
  890. void
  891. DoContentClick(
  892.         BrowserPtr                browserPtr
  893.     )
  894. {
  895.         DoClickInLog(ERROR_LOG, &EVENT);
  896.         DoClickInLog(RESULT_LOG, &EVENT);
  897. }
  898.  
  899. /*
  900.  * We don't have any content key down actions -- except that we should
  901.  * handle tabbing in/out of the panel.
  902.  */
  903. void
  904. DoWindowKeyDown(
  905.         BrowserPtr                browserPtr
  906.     )
  907. {
  908. #pragma unused (browserPtr)
  909. }
  910.  
  911. /*
  912.  * Call the AOM Panel and return TRUE if we must handle the event.. 
  913.  */
  914. Boolean
  915. DoPanelEvent(
  916.         BrowserPtr                browserPtr
  917.     )
  918. {
  919.         OSErr                    status;
  920.         Boolean                    result;
  921. #define AOM    (*BROWSER.aomPtr)
  922.  
  923.         result = TRUE;
  924.         if (BROWSER.aomPtr != NULL) {
  925.             status = AOMPanelEvent(BROWSER.aomPtr, &EVENT, &BROWSER.whatHappened);
  926.             if (status == noErr) {
  927.                 DisplayEventString(browserPtr); 
  928.                 result = ((BROWSER.whatHappened & kAOMAppMustHandleEventMask) != 0);
  929.                 /* TBS: handle button hits */
  930.                 if ((BROWSER.whatHappened & kAOMSelectedAnItemMask) != 0) {
  931.                     DisplayLogString(ERROR_LOG, "\pMain: selected");
  932.                 }
  933.                 if ((BROWSER.whatHappened & kAOMChangedSelectionMask) != 0) {
  934.                     DisplayLogString(ERROR_LOG, "\pMain: changed");
  935.                 }
  936.                 if ((BROWSER.whatHappened & kAOMAnyActionButtonHitMask) != 0) {
  937.                     DisplayLogString(ERROR_LOG, "\pMain: button");
  938.                     ProcessAOMSelection(browserPtr);
  939.                 }
  940.                 /*
  941.                  * This copies the state of the Find "searching" flag
  942.                  * to the window title. It is interesting only for debugging.
  943.                  */
  944.                 gFindInProgress =
  945.                     (BROWSER.whatHappened & kAOMFindInProgressMask) != 0;
  946.                 if (gFindInProgress != BROWSER.wasFinding) {
  947.                     BROWSER.wasFinding = gFindInProgress;
  948.                     SetWTitle(
  949.                         (WindowPtr) &WINDOW,
  950.                         (gFindInProgress)
  951.                             ? "\pSearch Started"
  952.                             : "\pSearch Stopped"
  953.                     );
  954.                 }
  955.             }
  956.         }
  957.         return (result);
  958. }
  959.  
  960. void
  961. ProcessAOMSelection(
  962.         register BrowserPtr        browserPtr
  963.     )
  964. {
  965.         OSErr                    status;
  966.         
  967.         if (BROWSER.selection != NULL) {
  968.             /*
  969.              * TBS: this is incorrect. We must dispose
  970.              * of the recipient list
  971.              */
  972.             DisposePtr((Ptr) BROWSER.selection);
  973.         }
  974.         BROWSER.selection = NULL;
  975.         BROWSER.selectionSize = 0;
  976.         BROWSER.recipientList = NULL;
  977.         status = AOMExtractSelection(
  978.                     BROWSER.aomPtr,
  979.                     &BROWSER.selectionSize,
  980.                     &BROWSER.selection
  981.                 );
  982.         LogStatus(ERROR_LOG, status, "\pAOMExtractSelection");
  983.         if (status == noErr) {
  984. #if USE_MAIL_ADDRESSES
  985.             status = SMPResolveToRecipient(
  986.                         BROWSER.selection,
  987.                         &BROWSER.recipientList,
  988.                         BROWSER.userIdentity
  989.                     );
  990.             LogStatus(ERROR_LOG, status, "\pSMPResolveToRecipient");
  991.             if (status == noErr)
  992.                 DisplayRecipients(browserPtr);
  993.             else {
  994.                 NOTE("\p• SMPResolveToRecipient failed, DSSpec:");
  995.                 DisplayOneDSSpec(browserPtr, BROWSER.selection);
  996.             }
  997. #else
  998.             NOTE("\p• DSSpec");
  999.             DisplayOneDSSpec(browserPtr, BROWSER.selection);
  1000. #endif            
  1001.         }
  1002. }
  1003.  
  1004. /*
  1005.  * Extract the recipients from BROWSER.recipientList and display
  1006.  * them in the error log area.
  1007.  */
  1008. void
  1009. DisplayRecipients(
  1010.         register BrowserPtr        browserPtr
  1011.     )
  1012. {
  1013.         SMPRecipientDescriptorPtr    recipPtr;
  1014. #define RECIP    (*recipPtr)
  1015.  
  1016.         DisplayLogString(RESULT_LOG, "\p•");
  1017.         for (recipPtr = BROWSER.recipientList;
  1018.                 recipPtr != NULL;
  1019.                 recipPtr = RECIP.next) {
  1020.             /*
  1021.              * Extract the record name from RECIP.theAddress. This is of type
  1022.              * MailRecipient which is, ultimately, of type DSSpec. So, to
  1023.              * summarize: the DSSpec contains an entity specifier which is
  1024.              * a pointer to a LocalRecordID. The actual recipient's name
  1025.              * is in the local record name field.
  1026.              */
  1027.             LOG_RString(
  1028.                 "\p• Name",
  1029.                 RECIP.theAddress.entitySpecifier->local.recordName
  1030.             );
  1031.             LOG_RString(
  1032.                 "\p• Type",
  1033.                 RECIP.theAddress.entitySpecifier->local.recordType
  1034.             );
  1035.             DisplayOneDSSpec(browserPtr, (PackedDSSpecPtr) RECIP.recipient);
  1036.         }
  1037. }
  1038.  
  1039. void
  1040. DisplayOneDSSpec(
  1041.         register BrowserPtr            browserPtr,
  1042.         PackedDSSpecPtr                theDSSpecPtr
  1043.     )
  1044. {
  1045.         DSSpecDumpRecord            theDSSpecDumpRecord;
  1046.  
  1047.         UnpackPackedDSSpec(browserPtr,theDSSpecPtr, &theDSSpecDumpRecord);
  1048.         DisplayUnpackedDSSpec(browserPtr, &theDSSpecDumpRecord);
  1049.         DisposeDSSpecDumpRecord(browserPtr, &theDSSpecDumpRecord);            
  1050.  
  1051. }
  1052.  
  1053. void
  1054. DisplayEventString(
  1055.         register BrowserPtr            browserPtr
  1056.     )
  1057. {
  1058.         register EventStringPtr        ep;
  1059.         static Boolean                wasFinding;
  1060.         Boolean                        isFinding;
  1061.         
  1062.         for (ep = (EventStringPtr) gEventString; ep->stateBit != 0; ep++) {
  1063.             if ((ep->stateBit & BROWSER.whatHappened) != 0)
  1064.                 DisplayLogString(ERROR_LOG, ep->message);
  1065.         }
  1066.         isFinding = (BROWSER.whatHappened & kAOMFindInProgressMask) != 0;
  1067.         if (wasFinding != isFinding) {
  1068.             DisplayLogString(
  1069.                 ERROR_LOG,
  1070.                 (isFinding)
  1071.                     ? "\pFinding started"
  1072.                     : "\pFinding stopped"
  1073.             );
  1074.             wasFinding = isFinding;
  1075.         }
  1076. }
  1077.  
  1078. /*
  1079.  * Get the user's local identity. On success, the identity is
  1080.  * stored in the specified location. The only expected error
  1081.  * is userCanceledErr, which probably means "just exit the
  1082.  * application." Other errors should be displayed:
  1083.  * Return noErr                        Installed and available.
  1084.  * Return userCanceledErr            User canceled identity prompt
  1085.  * Return kOCEToolboxNotOpen         Installed, but not available,
  1086.  * Return gestaltUndefSelectorErr    Not present on this machine
  1087.  */
  1088. OSErr
  1089. GetUserIdentity(
  1090.         AuthIdentity            *userIdentity
  1091.     )
  1092. {
  1093.         long                    gestaltResponse;
  1094.         SDPIdentityKind            selectedKind;
  1095.         AuthParamBlock            authParamBlock;
  1096.         OSErr                    status;
  1097.         
  1098.         /*
  1099.          * Make sure this Macintosh supports AOCE.
  1100.          */
  1101.         status = Gestalt(gestaltOCEToolboxAttr, &gestaltResponse);
  1102.         if (status == noErr
  1103.          && (gestaltResponse & gestaltOCETBAvailable) == 0)
  1104.             status = kOCEToolboxNotOpen;
  1105.         if (status == noErr) {
  1106.             /*
  1107.              * OCE Setup: get the user's local authentication identity. If it's
  1108.              * already set, we can do this silently. If it's not set (the system
  1109.              * has just been started), we'll prompt for the user's identity
  1110.              * and password.
  1111.              *
  1112.              * The only expected error is "Cancel" from SDPPromptForIdentity.
  1113.              */
  1114.             status = AuthGetLocalIdentity(
  1115.                     &authParamBlock,            /* Parameter block                */
  1116.                     FALSE                        /* Synchronous                    */
  1117.                 );
  1118.         }
  1119.         if (status == noErr) {
  1120.             /*
  1121.              * The user identity has already been specified.
  1122.              */
  1123.             *userIdentity = authParamBlock.getLocalIdentityPB.theLocalIdentity;
  1124.         }
  1125.         else if (status == kOCELocalAuthenticationFail) {
  1126.             /*
  1127.              * No user identity has been specified. Ask for the user to specify
  1128.              * the local identity.
  1129.              */
  1130.             status = SDPPromptForID(
  1131.                     userIdentity,            /* AuthIdentity        *id                */ 
  1132.                     NULL,                    /* Default guest prompt                */
  1133.                     NULL,                    /* Default specific prompt            */
  1134.                     NULL,                    /* Default local prompt                */
  1135.                     NULL,                    /* RString            *recordType        */
  1136.                     (                        /* SDPIdentityKind    permittedKinds    */
  1137.                         kSDPLocalIdentityMask        /* Local identity            */
  1138.                         | kSDPSpecificIdentityMask    /* or specific identity        */
  1139.                         | kSDPGuestMask                /* or guest identity        */
  1140.                     ),
  1141.                     &selectedKind,            /* SDPIdentityKind    *selectedKind    */
  1142.                     NULL,                    /* RecordID            *loginFilter    */
  1143.                     0                        /* SDPLoginFilterKind filterKind    */                        
  1144.                 );
  1145.         }
  1146.         else {
  1147.             /* AuthGetLocalIdentity is very unhappy. */
  1148.         }
  1149.         return (status);
  1150. }
  1151.  
  1152. /*
  1153.  * Make sure this Macintosh supports AOCE.
  1154.  */
  1155. void
  1156. CheckForAOCEToolbox(void)
  1157. {
  1158.         OSErr                status;
  1159.         long                gestaltResponse;
  1160.         
  1161.         status = Gestalt(gestaltOCEToolboxAttr, &gestaltResponse);
  1162.         if (status == noErr
  1163.          && (gestaltResponse & gestaltOCETBAvailable) == 0)
  1164.             status = kOCEToolboxNotOpen;
  1165.         gHasAOCEToolbox = (status == noErr);
  1166. }
  1167.  
  1168.  
  1169. /*
  1170.  * Debug: write the AuxColorRecord table to the error log.
  1171.  */
  1172. void
  1173. DumpAuxColorRecord(
  1174.         register BrowserPtr            browserPtr
  1175.     )
  1176. {
  1177.         AuxWinHandle                auxWinHandle;
  1178.         CTabHandle                    ctabHandle;
  1179.         AuxCtlHandle                acHandle;
  1180.         CCTabHandle                    cctabHandle;
  1181.         
  1182.         short                        i;
  1183.         Str255                        work;
  1184.         Str255                        value;
  1185.         RGBColor                    saveForeColor;
  1186.         RGBColor                    saveBackColor;
  1187.         RGBColor                    foreColor;
  1188.         RGBColor                    backColor;
  1189. #define CTAB    (**ctabHandle)
  1190. #define CTABLE    (CTAB.ctTable)
  1191.  
  1192.         (void) GetAuxWin((WindowPtr) &WINDOW, &auxWinHandle);
  1193.         if (auxWinHandle != NULL) {
  1194.             DisplayLogString(ERROR_LOG, "\p• Window Color Table");
  1195.             ctabHandle = (**auxWinHandle).awCTable;
  1196.             for (i = 0; i <= CTAB.ctSize; i++) {
  1197.                 NumToString(i, work);
  1198.                 pstrcat(work, "\p = ");
  1199.                 NumToString(CTABLE[i].value, value);
  1200.                 pstrcat(work, value);
  1201.                 pstrcat(work, "\p ");
  1202.                 DumpColor(&CTABLE[i].rgb, work);
  1203.                 DisplayLogString(ERROR_LOG, work);
  1204.             }
  1205.         }
  1206. #undef CTAB
  1207. #undef CTABLE
  1208. #define CTAB    (**cctabHandle)
  1209. #define CTABLE    (CTAB.ctTable)
  1210.         (void) GetAuxCtl(NULL, &acHandle);
  1211.         if (acHandle != NULL) {
  1212.             DisplayLogString(ERROR_LOG, "\p• Default Control Color");
  1213.             cctabHandle = (**acHandle).acCTable;
  1214.             for (i = 0; i <= CTAB.ctSize; i++) {
  1215.                 NumToString(i, work);
  1216.                 pstrcat(work, "\p = ");
  1217.                 NumToString(CTABLE[i].value, value);
  1218.                 pstrcat(work, value);
  1219.                 pstrcat(work, "\p ");
  1220.                 DumpColor(&CTABLE[i].rgb, work);
  1221.                 DisplayLogString(ERROR_LOG, work);
  1222.             }
  1223.         }
  1224. #undef CTAB
  1225. #undef CTABLE
  1226.         GetForeColor(&saveForeColor);
  1227.         GetBackColor(&saveBackColor);
  1228.         PenNormal();
  1229.         foreColor.red = foreColor.green = foreColor.blue = 0;        /* Black    */
  1230.         backColor.red = backColor.green = backColor.blue = 65535;    /* White    */
  1231.         DisplayLogString(ERROR_LOG, "\p• Gray -> White");
  1232.         while (GetGray(GetGDevice(), &backColor, &foreColor)) {
  1233.             work[0] = 0;
  1234.             DumpColor(&foreColor, work);
  1235.             DisplayLogString(ERROR_LOG, work);
  1236.         }
  1237.         foreColor.red = foreColor.green = foreColor.blue = 65535;    /* White    */
  1238.         backColor.red = backColor.green = backColor.blue = 0;        /* Black    */
  1239.         DisplayLogString(ERROR_LOG, "\p• Gray -> Black");
  1240.         while (GetGray(GetGDevice(), &backColor, &foreColor)) {
  1241.             work[0] = 0;
  1242.             DumpColor(&foreColor, work);
  1243.             DisplayLogString(ERROR_LOG, work);
  1244.         }
  1245.         RGBForeColor(&saveForeColor);
  1246.         RGBBackColor(&saveBackColor);
  1247. }
  1248.  
  1249. void
  1250. DumpColor(
  1251.         RGBColor                    *theColor,
  1252.         StringPtr                    result
  1253.     )
  1254. {
  1255.         Str255                        value;
  1256.  
  1257.         pstrcat(result, "\p[");
  1258.         NumToString(theColor->red, value);
  1259.         pstrcat(result, value);
  1260.         pstrcat(result, "\p, ");
  1261.         NumToString(theColor->green, value);
  1262.         pstrcat(result, value);
  1263.         pstrcat(result, "\p, ");
  1264.         NumToString(theColor->blue, value);
  1265.         pstrcat(result, value);
  1266.         pstrcat(result, "\p]");
  1267. }
  1268.